package middleware

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	"github.com/sirupsen/logrus"
	"net/http"
	"strconv"
	"todo-list/constants"
	"todo-list/repository/cache"
	"todo-list/utils/auth"
	"todo-list/utils/responses"
)

func AuthenticationMiddleWare() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		token := ctx.GetHeader("Authorization")
		if token == "" {
			ctx.JSON(http.StatusForbidden, responses.RespError(nil, constants.CodeNoToken))
			ctx.Abort()
			return
		}
		userIdStr, err := cache.Get(cache.TokenKey(token))
		// todo refresh token according to refresh-token record
		// todo use this to refresh token: ctx.Header("Authorization", "new token")
		if errors.Is(err, redis.ErrNil) {
			token, err = refreshToken(token)
			if err != nil {
				logrus.Errorln(err.Error())
				ctx.JSON(http.StatusForbidden, responses.RespError(err, constants.CodeTokenTimeout))
				ctx.Abort()
				return
			}
			userIdStr, err = cache.Get(cache.TokenRefreshKey(token))
			ctx.Header("Authorization", token)
		}
		userId, err := strconv.Atoi(userIdStr)
		if err != nil {
			logrus.Errorln(err.Error())
			ctx.JSON(http.StatusForbidden, responses.RespError(err, constants.CodeAuthenticateError))
			ctx.Abort()
			return
		}
		ctx.Set("userId", uint(userId))
		ctx.Set("token", token)
		ctx.Next()
	}
}

func refreshToken(token string) (newToken string, err error) {
	userIdStr, err := cache.Get(cache.TokenRefreshKey(token))
	if err != nil {
		return
	}
	newToken, err = auth.GenerateToken()
	if err != nil {
		return
	}
	err = cache.SetWithExpire(cache.TokenKey(newToken), userIdStr, auth.TokenTimeout)
	if err != nil {
		return "", err
	}
	_, err = cache.Do("Rename", cache.TokenRefreshKey(token), cache.TokenRefreshKey(newToken))
	if err != nil {
		return "", err
	}
	return
}
